home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 June: ROMin Holiday / ADC Developer CD (1992-06) (''ROMin Holiday'')_iso / Developer Connection - 06-1992.iso / Periodicals / develop / develop 10 code / Is it Art? / ArtMaker / PainterlyUtils.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-04-08  |  18.7 KB  |  719 lines  |  [TEXT/KAHL]

  1. #include    "Painterly.h"
  2.  
  3. /* Painterly Globals */
  4. extern DocumentRecord    gSrcDoc, gDstDoc;        /* The doc records */
  5. extern CWindowPtr        gSrcWindPtr, gDstWindPtr, gUndoTarget;
  6. extern Handle            gCurrentBrushHandle;    /* handle to the current Brush code */
  7. extern BrushParams        gBrushStuff;            /* the parameter structure for brush calls */
  8. extern GWorldPtr        gUndoBuffer;            /* a buffer to allow undo */
  9. extern RGBColor            gBGColor;
  10.  
  11. /* These globals are used only by the pict spooling procedures */
  12. extern OSErr            gPictError;
  13. extern long                gPictSize;
  14. extern PicHandle        gPictHandle;
  15. extern short            gPictFileRef;
  16.  
  17.  
  18. /* Returns true if the given window is one of ours */
  19. Boolean     IsAppWindow(WindowPtr wind)
  20. {
  21.     Boolean    rslt;
  22.     
  23.     if(wind == gSrcWindPtr || wind == gDstWindPtr)
  24.         rslt = true;
  25.     else
  26.         rslt = false;
  27.     
  28.     return rslt;
  29. }
  30.  
  31.  
  32. /* Paints a brush stroke both in the offscreen world and in the destination window.
  33. The point passed in here is in the GWorld's coordinates, so it needs to be converted
  34. to the window's coordinates when we draw there */
  35. void StrokeBoth(Point pt)
  36. {
  37.     short            ignoredErr;
  38.     CGrafPtr        oldport;
  39.     GDHandle        olddev;
  40.     long            seed;
  41.     DocumentPeek    doc;
  42.     
  43.     GetGWorld(&oldport, &olddev);
  44.     if(LockBoth())
  45.     {
  46.         /* Install the point */
  47.         gBrushStuff.pt = pt;
  48.         
  49.         /* Get the color from the source */
  50.         SetGWorld(gSrcDoc.world, nil);
  51.         GetCPixel(pt.h, pt.v, &gBrushStuff.color);
  52.         
  53.         /* get the current seed */
  54.         seed = randSeed;
  55.  
  56.         /* Set port to Offworld and draw */
  57.         SetGWorld(gDstDoc.world, nil);
  58.         ignoredErr = CallBrush(kDoStroke, &gBrushStuff, gCurrentBrushHandle);
  59.         
  60.         /* Set port to screen, reset Random seed, change origin momentarily, and draw again */
  61.         SetGWorld(gDstWindPtr, olddev);
  62.         randSeed = seed;
  63.         doc = (DocumentPeek)gDstWindPtr;
  64.         SetOrigin(GetCtlValue(doc->hScroll), GetCtlValue(doc->vScroll));
  65.         CloseClip(gDstWindPtr);    /* Set Clip to window minus scrollBars */
  66.         ignoredErr = CallBrush(kDoStroke, &gBrushStuff, gCurrentBrushHandle);
  67.         SetOrigin(0, 0);
  68.         ClipRect(&gDstWindPtr->portRect);    /* Reset Clip */
  69.     
  70.         /* Unlock everything */
  71.         UnlockBoth();
  72.         
  73.         /* Set dirty flag */
  74.         gDstDoc.dirty = true;
  75.     }
  76.     SetGWorld(oldport, olddev);
  77. }
  78.  
  79. /* Copies a GWorld to its associated window */    
  80. void OffToWindow(CWindowPtr wind, Rect *rect)
  81. {
  82.     GWorldPtr    world;
  83.     Rect        srcRect;
  84.     Point         srcOrigin;
  85.     
  86.     SetPort(wind);
  87.     
  88.     /* Get the the offscreen coordinates of the top left of the window 
  89.         from the scrollBars. */
  90.     srcOrigin.h = GetCtlValue( ((DocumentPeek)wind)->hScroll );
  91.     srcOrigin.v = GetCtlValue( ((DocumentPeek)wind)->vScroll );
  92.     
  93.     /* Get the offscreen rect that needs to be copied */
  94.     srcRect = *rect;
  95.     OffsetRect(&srcRect, srcOrigin.h, srcOrigin.v);
  96.     
  97.     world = ((DocumentPeek)wind)->world;
  98.     if(world != nil)
  99.     {
  100.         if(LockWorld(world))
  101.         {
  102.             ForeColor(blackColor);
  103.             BackColor(whiteColor);
  104.             CopyBits(*GetGWorldPixMap(world), *(wind->portPixMap),
  105.                     &srcRect, rect, srcCopy, nil);
  106.             UnlockWorld(world);
  107.         }
  108.     }
  109. }
  110.  
  111. /* Copies one GWorld to another */
  112. void OffToOff(GWorldPtr src, GWorldPtr dst)
  113. {
  114.     CGrafPtr    oldport;
  115.     GDHandle    olddev;
  116.     
  117.     if(src == nil || dst == nil)
  118.         return;
  119.  
  120.     GetGWorld(&oldport, &olddev);
  121.     SetGWorld(dst, nil);
  122.     if(LockBoth())
  123.     {
  124.         ForeColor(blackColor);
  125.         BackColor(whiteColor);
  126.         CopyBits(*GetGWorldPixMap(src), *GetGWorldPixMap(dst),
  127.                 &src->portRect, &dst->portRect, srcCopy, nil);
  128.         UnlockBoth();
  129.     }
  130.     SetGWorld(oldport, olddev);
  131. }
  132.  
  133. /* Erases a GWorld. If it's the destination, it paints it with the background color */
  134. void EraseOff(GWorldPtr world)
  135. {
  136.     CGrafPtr    oldport;
  137.     GDHandle    olddev;
  138.     
  139.     GetGWorld(&oldport, &olddev);
  140.     SetGWorld(world, nil);
  141.     if(LockWorld(world))
  142.     {
  143.         if(world == gSrcDoc.world)
  144.             EraseRect(&world->portRect);
  145.         else
  146.         {
  147.             RGBForeColor(&gBGColor);
  148.             PaintRect(&world->portRect);
  149.         }
  150.         UnlockWorld(world);
  151.     }
  152.     SetGWorld(oldport, olddev);
  153. }
  154.  
  155. /* Draws a picture into a GWorld */
  156. void DrawPictOff(GWorldPtr world, PicHandle pict)
  157. {
  158.     CGrafPtr        oldport;
  159.     GDHandle        olddev;
  160.     Rect            frame;
  161.     
  162.     if(pict == nil || world == nil)
  163.         return;
  164.     
  165.     frame = (*pict)->picFrame;
  166.     OffsetRect(&frame, -frame.left, -frame.top);
  167.     GetGWorld(&oldport, &olddev);
  168.     SetGWorld(world, nil);
  169.     if(LockWorld(world))
  170.     {
  171.         DrawPicture(pict, &frame);
  172.         UnlockWorld(world);
  173.     }
  174.     SetGWorld(oldport, olddev);
  175. }
  176.  
  177. /* Locks a GWorld's Pixels */
  178. Boolean LockWorld(GWorldPtr world)
  179. {
  180.     PixMapHandle    pix;
  181.     
  182.     pix = GetGWorldPixMap(world);
  183.     return(LockPixels(pix));
  184. }
  185.  
  186. /* Unlocks a GWorld's Pixels */
  187. void UnlockWorld(GWorldPtr world)
  188. {
  189.     PixMapHandle    pix;
  190.     
  191.     pix = GetGWorldPixMap(world);
  192.     UnlockPixels(pix);
  193. }
  194.  
  195. /* Locks both our GWorld's Pixels */
  196. Boolean LockBoth(void)
  197. {
  198.     Boolean        rslt = false;
  199.     
  200.     if(LockWorld(gSrcDoc.world))
  201.         if(LockWorld(gDstDoc.world))
  202.             rslt = true;
  203.         else
  204.             UnlockWorld(gSrcDoc.world);
  205.     
  206.     return rslt;
  207. }
  208.  
  209. /* Unlocks both our GWorld's Pixels */
  210. void UnlockBoth(void)
  211. {
  212.     UnlockWorld(gSrcDoc.world);
  213.     UnlockWorld(gDstDoc.world);
  214. }
  215.  
  216. /* This routine gets any pending activate and update events from the queue and processes
  217. them. Used when saving changes, when a window is brought to the front behind a dialog */
  218. void DoActivateUpdate(void)
  219. {
  220.     EventRecord        event;
  221.     GrafPtr            oldport;
  222.     
  223.     /* First do activates */
  224.     while(GetNextEvent(activMask, &event) == true)
  225.     {
  226.         AppActivate((WindowPtr)event.message, event.modifiers & activeFlag);
  227.     }
  228.     
  229.     /* Then updates */
  230.     while(GetNextEvent(updateMask, &event) == true)
  231.     {
  232.         GetPort(&oldport);
  233.         SetPort((WindowPtr)event.message);
  234.         BeginUpdate((WindowPtr)event.message);
  235.         
  236.         AppUpdate(&event);
  237.         
  238.         EndUpdate((WindowPtr)event.message);
  239.         SetPort(oldport);
  240.     }
  241. }
  242.     
  243.  
  244. /* Stolen from Craig Prouse's famous DoWZoom routine, and modified for my own devious 
  245.     purposes. I tried to copy the Finder's behavior: If I can just size the window bigger 
  246.     and the result will stay completely on the same device the window is already on, I do 
  247.     that. Otherwise the window is zoomed to the top left of the appropriate device. This 
  248.     routine assumes up front that we are running in a Color Quickdraw environment, i.e
  249.     there is the possibility of multiple devices. It does NOT zoom the window, it just
  250.     calculates the appropriate rect and installs it in the window's stdState field */
  251. void ReadyWZoom(WindowPtr wind, short zoomDir, short maxH, short maxV)
  252. {
  253.     /* Only if we are zooming out do we need to go through all these gyrations */
  254.     if(zoomDir == inZoomOut)
  255.     {
  256.         Rect        globalPortRect, theSect, zoomRect, deviceRect;
  257.         GDHandle    nthDevice, dominantGDevice;
  258.         long        sectArea, greatestArea;
  259.         Boolean        oneDevice;
  260.         
  261.         globalPortRect = wind->portRect;
  262.         LocalToGlobal(&topLeft(globalPortRect));
  263.         LocalToGlobal(&botRight(globalPortRect));
  264.         
  265.         /* Walk the device list looking for the device that contains most of the 
  266.             window, or all of the window */
  267.         nthDevice = GetDeviceList();
  268.         greatestArea = 0;
  269.         while(nthDevice != nil)
  270.         {
  271.             SectRect(&globalPortRect, &(*nthDevice)->gdRect, &theSect);
  272.             
  273.             /* if the intersection is equal to the portRect, then the window is fully
  274.                 contained on this device: we can stop looking */
  275.             if(EqualRect(&globalPortRect, &theSect))
  276.             {
  277.                 dominantGDevice = nthDevice;
  278.                 break;
  279.             }
  280.             sectArea = (long)(theSect.right - theSect.left) * (theSect.bottom - theSect.top);
  281.             if(sectArea > greatestArea)
  282.             {
  283.                 greatestArea = sectArea;
  284.                 dominantGDevice = nthDevice;
  285.             }
  286.             nthDevice = GetNextDevice(nthDevice);
  287.         }
  288.         
  289.         /* Now let's see if we can just increase the window's size to the max and stay
  290.             completely on the dominantGDevice */
  291.         SetRect(&zoomRect, globalPortRect.left, globalPortRect.top,
  292.                     globalPortRect.left + maxH, globalPortRect.top + maxV);
  293.         deviceRect = (*dominantGDevice)->gdRect;
  294.         InsetRect(&deviceRect, 4, 4); /* Keep window 4 pixels away from edges */
  295.         SectRect(&zoomRect, &deviceRect, &theSect);
  296.  
  297.         /* if the intersection is equal to the zoomRect, then the zoomRect is fully
  298.             contained on this device: we have our new zoomRect. Otherwise we need to
  299.             do a little more work. */
  300.         if(!EqualRect(&zoomRect, &theSect))
  301.         {
  302.             short    bias;
  303.             
  304.             /* Calculate the height of the window's title bar. Actually, in this app
  305.                 I already have, but in the interests of easy cut and paste I'll do 
  306.                 it again */
  307.             bias = globalPortRect.top - 1 - (*(((WindowPeek)wind)->strucRgn))->rgnBBox.top;
  308.             
  309.             /* Now we can create our zoom rectangle. Account for MBarHeight if on the
  310.                 main device */
  311.             if(dominantGDevice == GetMainDevice())
  312.                 bias += GetMBarHeight();
  313.             zoomRect.left = deviceRect.left;
  314.             zoomRect.top = deviceRect.top + bias;
  315.             
  316.             /* Set right and bottom to either the max size or the deviceRect,
  317.                 whichever is less */
  318.             if(zoomRect.left + maxH > deviceRect.right)
  319.                 zoomRect.right = deviceRect.right;
  320.             else
  321.                 zoomRect.right = zoomRect.left + maxH;
  322.             
  323.             if(zoomRect.top + maxV > deviceRect.bottom)
  324.                 zoomRect.bottom = deviceRect.bottom;
  325.             else
  326.                 zoomRect.bottom = zoomRect.top + maxV;
  327.         }
  328.         
  329.         /* Load the rect into the stdState of the window */
  330.         (*(WStateDataHandle)(((WindowPeek)wind)->dataHandle))->stdState = zoomRect;
  331.       }
  332. }
  333.  
  334. /* Releases the GWorlds, and sets them to nil */
  335. void KillGlobalGWorlds(void)
  336. {
  337.      /* Need to do this in reverse order (I think), since the Dst and Undo Buffer 
  338.          share a device with the Src. */
  339.     if(gUndoBuffer != nil)
  340.     {
  341.         DisposeGWorld(gUndoBuffer);
  342.         gUndoBuffer = nil;
  343.     }
  344.     if(gDstDoc.world != nil)
  345.     {
  346.         DisposeGWorld(gDstDoc.world);
  347.         gDstDoc.world = nil;
  348.     }
  349.     if(gSrcDoc.world != nil)
  350.     {
  351.         DisposeGWorld(gSrcDoc.world);
  352.         gSrcDoc.world = nil;
  353.     }
  354. }
  355.  
  356. /* Attempts to allocate the needed GWorlds */
  357. Boolean InitGlobalGWorlds(Rect *frame)
  358. {
  359.     CGrafPtr    oldport;
  360.     GDHandle    olddev;
  361.     short        offdepth, rslt = false;
  362.     QDErr         err;
  363.     Rect        newRect;
  364.  
  365.     GetGWorld(&oldport, &olddev);
  366.     
  367.     KillGlobalGWorlds();
  368.     
  369.     /* Ensure that topLeft is 0, 0 */
  370.     newRect = *frame;
  371.     OffsetRect(&newRect, -newRect.left, -newRect.top);
  372.     
  373.     /* Try for the deepest depth we can get */
  374.     offdepth = 32;
  375.     while(true)
  376.     {
  377.         err = NewGWorld(&gSrcDoc.world, offdepth, &newRect, nil, nil, 0L);
  378.         if(err == noErr)
  379.         {
  380.             err = NewGWorld(&gDstDoc.world, offdepth, &newRect, nil,
  381.                                 GetGWorldDevice(gSrcDoc.world), noNewDevice);
  382.             if((err == noErr))
  383.             {
  384.                 rslt = true;
  385.                 break;
  386.             }
  387.             else
  388.                 DisposeGWorld(gSrcDoc.world); /* First one worked, so kill it */
  389.         }
  390.         /* If we are here, there was a problem. Reduce offdepth and try again */
  391.         offdepth >>= 1;
  392.         if(offdepth < 1) /* Tried everything, no go */
  393.             break;
  394.     }
  395.     if(rslt == true)    /* Success! */
  396.     {
  397.         /* Alert user if shallow depth */
  398.         if(offdepth < 32)
  399.             DepthAlert(offdepth);
  400.  
  401.         /* Clear the worlds */
  402.         EraseOff(gSrcDoc.world);
  403.         EraseOff(gDstDoc.world);
  404.         
  405.         /* Try to recreate the Undo buffer */
  406.         if( NewGWorld(    &gUndoBuffer, 
  407.                         offdepth, 
  408.                         &newRect, 
  409.                         nil, 
  410.                         GetGWorldDevice(gSrcDoc.world), 
  411.                         noNewDevice) == noErr)
  412.         {
  413.             EraseOff(gUndoBuffer);
  414.         }
  415.         else
  416.         {
  417.             gUndoBuffer = nil;
  418.             DoErrorAlert(kNoUndoStr, 0);
  419.         }
  420.     }
  421.     SetGWorld(oldport, olddev);
  422.     return rslt;
  423. }
  424.  
  425. /* Converts a GWorld to a PICT */
  426. PicHandle WorldToPict(GWorldPtr world)
  427. {
  428.     CGrafPtr    oldport;
  429.     GDHandle    olddev;
  430.     PicHandle    pict = nil;
  431.     
  432.     if(world == nil)
  433.         return nil;
  434.         
  435.     GetGWorld(&oldport, &olddev);
  436.     SetGWorld(world, nil);
  437.     
  438.     if(LockWorld(world))
  439.     {
  440.         ForeColor(blackColor);
  441.         pict = OpenPicture(&world->portRect);
  442.         CopyBits(*GetGWorldPixMap(world), *GetGWorldPixMap(world), 
  443.                 &world->portRect, &world->portRect, srcCopy, nil);
  444.         ClosePicture();
  445.         UnlockWorld(world);
  446.     }
  447.     SetGWorld(oldport, olddev);
  448.     
  449.     /* Check for a bad pict */
  450.     if((**pict).picSize > 0)
  451.         return pict;
  452.     else
  453.     {
  454.         KillPicture(pict);
  455.         return nil;
  456.     }
  457. }
  458.  
  459. /* Sets the clip of the window to exclude the scroll bars. Assumes the port is set 
  460. already */
  461. void CloseClip(WindowPtr wind)
  462. {
  463.     Rect    clip;
  464.     
  465.     clip = wind->portRect;
  466.     clip.right -= kScrollAdjust;
  467.     clip.bottom -= kScrollAdjust;
  468.     ClipRect(&clip);
  469. }
  470.  
  471. /*---------------------------------------------------------------------------
  472.     Scroll Bar routines: Mostly stolen from TESample, but heavily modified, 
  473.     so even their mother wouldn't know 'em.
  474. ---------------------------------------------------------------------------*/
  475.  
  476. /* Here we resize the scrollbars (if needed) and adjust their values. It's a good 
  477. idea to hide the controls before calling this routine, otherwise
  478. it'll probably be pretty ugly for the user */
  479. void AdjustScrollbars(WindowPtr wind, Boolean needsResize)
  480. {
  481.     DocumentPeek doc;
  482.     
  483.     doc = (DocumentPeek)wind;
  484.     
  485.     /* First move and resize the bars, if needed */
  486.     if ( needsResize )
  487.     {
  488.         /* First the horizontal one. Scroll bars actually hang over the edge of the 
  489.         window by one pixel, for some sick reason known only to the people who invented
  490.         it. So every Mac programmer on earth has to discover this somehow (I did it by 
  491.         trial and error). Why isn't this in Inside Mac?) */
  492.     
  493.         MoveControl(doc->hScroll, -1, wind->portRect.bottom - kScrollAdjust);
  494.         SizeControl(doc->hScroll, (wind->portRect.right - 
  495.                     wind->portRect.left) - (kScrollAdjust - kScrollTweak),
  496.                     kScrollWidth);
  497.         
  498.         /* Now the vertical one */
  499.         MoveControl(doc->vScroll, wind->portRect.right - kScrollAdjust, -1);
  500.         SizeControl(doc->vScroll, kScrollWidth, (wind->portRect.bottom - 
  501.                     wind->portRect.top) - (kScrollAdjust - kScrollTweak));
  502.     }
  503.     
  504.     /* Now adjust the values of the scrollbars */
  505.     AdjustScrollValues(wind);
  506. }
  507.  
  508. /* Adjusts the values of the scroll bars. */
  509. void AdjustScrollValues(WindowPtr wind)
  510. {
  511.     short            lines, max;
  512.     short            oldValue;
  513.     Rect            viewRect;            
  514.     DocumentPeek     doc;
  515.     
  516.     doc = (DocumentPeek)wind;
  517.     
  518.     /* viewRect is the port of the window minus the scrollbars */
  519.     viewRect = wind->portRect;
  520.     viewRect.right -= kScrollAdjust;
  521.     viewRect.bottom -= kScrollAdjust;
  522.  
  523.     /* First Vertical */
  524.     oldValue = GetCtlValue(doc->vScroll);
  525.     /* lines is the number of lines (pixels) in the offscreen world */
  526.     lines = doc->world->portRect.bottom;
  527.     /* max is the amount of the picture that can't be seen */
  528.     max = lines - viewRect.bottom;
  529.     if ( max < 0 ) max = 0;
  530.     SetCtlMax(doc->vScroll, max);
  531.     
  532.     /*     Adjust scroll value if necessary. It's necessary only if the current control 
  533.         value (which is also the number of lines scrolled off the top) plus the visible
  534.         vertical lines in the window is greater than the total picture height. If so,
  535.         then just set the control value to the max, i.e. scrolled all the way down. */
  536.     if(oldValue + viewRect.bottom > lines)
  537.         SetCtlValue(doc->vScroll, max);
  538.  
  539.     /* Now Horizontal */
  540.     oldValue = GetCtlValue(doc->hScroll);
  541.     /* lines is the number of horiz. pixels in the offscreen world */
  542.     lines = doc->world->portRect.right;
  543.     /* max is the amount of the picture that can't be seen */
  544.     max = lines - viewRect.right;
  545.     if ( max < 0 ) max = 0;
  546.     SetCtlMax(doc->hScroll, max);
  547.  
  548.     /*     Adjust scroll value if necessary. It's necessary only if the current control 
  549.         value (which is also the number of lines scrolled off the left) plus the visible
  550.         horiz. lines in the window is greater than the total picture width. If so,
  551.         then just set the control value to the max, i.e. scrolled all the way right. */
  552.     if(oldValue + viewRect.right > lines)
  553.         SetCtlValue(doc->hScroll, max);
  554. }
  555.  
  556.  
  557.  
  558. /* Determines how much to change the value of the scrollbar by and how
  559.     much to scroll the Pict, and then does both */
  560. pascal void ScrollActionProc(ControlHandle control, short part)
  561. {
  562.     short        amount, value, max;
  563.     WindowPtr    window;
  564.     
  565.     if ( part != 0 ) {                /* if it was actually in the control */
  566.         window = (*control)->contrlOwner;
  567.         switch ( part ) {
  568.             case inUpButton:
  569.             case inDownButton:        /* one line */
  570.                 amount = kLineScrollAmount;
  571.                 break;
  572.             case inPageUp:            /* one page */
  573.             case inPageDown:
  574.                 amount = kPageScrollAmount;
  575.                 break;
  576.         }
  577.         if ( (part == inDownButton) || (part == inPageDown) )
  578.             amount = -amount;        /* reverse direction for a downer */
  579.         
  580.         /* Pin the amount to the scroll bar range, set the value, and calculate the
  581.         change in value */
  582.         value = GetCtlValue(control);    /* get current value */
  583.         max = GetCtlMax(control);        /* and maximum value */
  584.         amount = value - amount;
  585.         if ( amount < 0 )
  586.             amount = 0;
  587.         else if ( amount > max )
  588.             amount = max;
  589.         SetCtlValue(control, amount);
  590.         amount = value - amount;        /* calculate the real change */
  591.         
  592.         /* Scroll the pict the appropriate amount */
  593.         if ( amount != 0 )
  594.         {
  595.             if(control == ((DocumentPeek)window)->hScroll)
  596.                 ScrollPict(amount, 0, window);
  597.             else
  598.                 ScrollPict(0, amount, window);
  599.         }
  600.     }
  601. } /* ScrollActionProc */
  602.  
  603. /* Scrolls the bits in the window by the given amount. */
  604. void ScrollPict(short hAmnt, short vAmnt, WindowPtr wind)
  605. {
  606.     RgnHandle    updateRgn;
  607.     Rect        viewRect;
  608.     
  609.     /* Make a new Region... */
  610.     updateRgn = NewRgn();
  611.     
  612.     /* ...get the rect of the window, minus the scrollBars... */
  613.     viewRect = wind->portRect;
  614.     viewRect.right -= kScrollAdjust;
  615.     viewRect.bottom -= kScrollAdjust;
  616.     
  617.     /* Scroll the window, saving the region that needs to be re-filled */
  618.     ScrollRect(&viewRect, hAmnt, vAmnt, updateRgn);
  619.     
  620.     /* Get the rect that encloses the region and draw to it */
  621.     if(updateRgn != nil)
  622.     {
  623.         viewRect = (*updateRgn)->rgnBBox;
  624.         DisposeRgn(updateRgn);
  625.     }
  626.     OffToWindow(wind, &viewRect);
  627. }
  628.  
  629.  
  630. /* Sets up an undo by copying the current window pixels to the Undo buffer and setting 
  631. the gUndoTarget to the window */
  632. void SetUpForUndo(CWindowPtr wind)
  633. {
  634.     if(gUndoBuffer != nil)
  635.     {
  636.         gUndoTarget = wind;
  637.         OffToOff(((DocumentPeek)wind)->world, gUndoBuffer);
  638.     }
  639. }
  640.  
  641. /* This routine replaces the given string with a string from the resource file */ 
  642. Str255 *TheStr(Str255 str, short index)
  643. {
  644.     GetIndString(str, kAppStringsID, index);
  645.     return str;
  646. }
  647.  
  648. /* This routine concatenates 2 pascal strings, adding a space between if addSpace is true */ 
  649. Str255 *PStrCat(Str255 str1, Str255 str2, Boolean addSpace)
  650. {
  651.     unsigned char    len1, len2, total;
  652.     
  653.     /* check lengths */
  654.     len1 = str1[0];
  655.     len2 = str2[0];
  656.     total = len1 + len2 + (addSpace ? 1 : 0);
  657.     if(total > 255)
  658.         return nil;
  659.     
  660.     if(addSpace)
  661.     {
  662.         str1[len1 + 1] = 0x20; /* Add the space */
  663.         len1 += 1;
  664.     }
  665.     BlockMove(&str2[1], &str1[len1 + 1], (long)len2);
  666.     str1[0] = total;
  667.     return str1;
  668. }
  669.  
  670. /* Just copies one string into another */
  671. void PStrCopy(Str255 src, Str255 dst)
  672. {
  673.     BlockMove(src, dst, (long)(src[0] + 1));
  674. }
  675.  
  676. void DoErrorAlert(short stringID1, short error)
  677. {
  678.     Str255    string1, string2;
  679.     
  680.     GetIndString(string1, kErrorStringsID, stringID1);
  681.     if(error != 0)
  682.         NumToString((long)error, string2);
  683.     else
  684.         *string2 = 0;
  685.  
  686.     ParamText(string1, string2, "\p", "\p");
  687.     StopAlert(kErrorAlertID, nil);
  688. }
  689.  
  690. /* -----------------------------------------------------
  691. Replacement bottlenecks for spooling Picts
  692. ------------------------------------------------------ */
  693.  
  694. pascal void    PictWriter(Ptr pointer, short nextHunk)
  695. {
  696.     long    hunkSize;
  697.     
  698.     if(gPictError == noErr)
  699.     {
  700.         hunkSize = nextHunk;
  701.         gPictError = FSWrite(gPictFileRef, &hunkSize, pointer);
  702.         gPictSize += hunkSize;
  703.         if(gPictHandle != nil)
  704.             (**gPictHandle).picSize = LoWord(gPictSize);
  705.     }
  706. }
  707.  
  708. pascal void    PictReader(Ptr pointer, short nextHunk)
  709. {
  710.     long    hunkSize;
  711.     
  712.     if(gPictError == noErr)
  713.     {
  714.         hunkSize = nextHunk;
  715.         gPictError = FSRead(gPictFileRef, &hunkSize, pointer);
  716.         /* IM says it is unclear how to handle errors here. Who am I to argue? */
  717.     }
  718. }
  719.